package cn.com.duiba.quanyi.open.demo.sm;

import cn.com.duiba.quanyi.open.demo.utils.Base62;
import com.sun.org.apache.xpath.internal.objects.XString;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.util.Base64;

/**
 * 国密sm2加密工具
 *
 * @author zzy
 * @create 2023/11/28 19:00
 **/
public class SM2Util {

    public static ThreadLocal<Signature> signature = ThreadLocal.withInitial(() -> {
        try {
            return Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), BouncyCastleProvider.PROVIDER_NAME);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    });

    static {
        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }

    public static KeyPair createKeyPair() {
        // 获取SM2 椭圆曲线推荐参数
        X9ECParameters ecParameters = GMNamedCurves.getByName("sm2p256v1");
        // 构造EC 算法参数
        ECNamedCurveParameterSpec sm2Spec = new ECNamedCurveParameterSpec(
                // 设置SM2 算法的 OID
                GMObjectIdentifiers.sm2p256v1.toString()
                // 设置曲线方程
                , ecParameters.getCurve()
                // 椭圆曲线G点
                , ecParameters.getG()
                // 大整数N
                , ecParameters.getN());
        try {
            // 创建 密钥对生成器
            KeyPairGenerator gen = KeyPairGenerator.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME);
            // 使用SM2的算法区域初始化密钥生成器
            gen.initialize(sm2Spec, new SecureRandom());
            // 获取密钥对
            KeyPair keyPair = gen.generateKeyPair();
            return keyPair;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 签名
     *
     * @param privateKey 签名私钥
     * @param plainText  明文
     * @return
     */
    private static String sign(PrivateKey privateKey, String plainText) {
        try {
            signature.get().initSign(privateKey);
            signature.get().update(plainText.getBytes());
            byte[] bytes = signature.get().sign();
            return Base62.getInstance().encode(bytes);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 签名
     *
     * @param privateKeyStr 签名私钥
     * @param plainText     明文
     * @return
     */
    public static String sign(String privateKeyStr, String plainText) {
        try {
            byte[] privateBytes = Base62.getInstance().decode(privateKeyStr);
            PrivateKey privateKey = BouncyCastleProvider.getPrivateKey(PrivateKeyInfo.getInstance(privateBytes));
            return sign(privateKey, plainText);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 验证签名
     *
     * @param publicKey 签名公钥
     * @param sign      签名结果
     * @param plainText 明文
     * @return
     */
    private static boolean verifySign(PublicKey publicKey, String sign, String plainText) {
        try {
            signature.get().initVerify(publicKey);
            signature.get().update(plainText.getBytes());
            return signature.get().verify(Base62.getInstance().decode(sign));
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 验证签名
     *
     * @param publicKeyStr 签名公钥
     * @param sign         签名结果
     * @param plainText    明文
     * @return
     */
    public static boolean verifySign(String publicKeyStr, String sign, String plainText) {
        try {
            byte[] publicBytes = Base62.getInstance().decode(publicKeyStr);
            PublicKey publicKey = BouncyCastleProvider.getPublicKey(SubjectPublicKeyInfo.getInstance(publicBytes));
            return verifySign(publicKey, sign, plainText);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        KeyPair keyPair = SM2Util.createKeyPair();
        byte[] pk = keyPair.getPrivate().getEncoded();
        String base64Key = Base64.getMimeEncoder(64, "\n".getBytes()).encodeToString(pk);
        System.out.println(base64Key);
        String sm2PrivateKey = Base62.getInstance().encode(pk);
        System.out.println("SM2私钥:（签名方请自行保存）");
        System.out.println(sm2PrivateKey); //

        String sm2PublicKey = Base62.getInstance().encode(keyPair.getPublic().getEncoded());
        System.out.println("SM2公钥:（请提供给验签方）");
        System.out.println(sm2PublicKey);
               sm2PrivateKey="1kRebqILWk9D7U8NiRRyAzQIUEqap3XuO1daMfj8taF3DzNV7Q2OjOg6cGIpwgoeFAJU8dmg0DrpgddgvqW6dVLEoV0wkMUeDfHHIG6KrPciJ7eyFK16N7TChIIAKAFQUH4li5rdzU4MGQIjg7JydzJ3EHip7pz2rbn0ondiPKWzk3McJIvi4wfHEYRNsOl8KBhp6HolAQ";
        sm2PublicKey="ZCbp55Y7NeQukYaMlr6XHd2FAAuCQSdXNNXN837A1Sw2odvqDNeOoiPB2J9jL5qja4HMgeGZs15Lb0fueraLXapfV41u7Z9WUyxU20tM9qvIBjf1ekkQo9LZli";
String privateKeyBase64 = Base64.getEncoder().encodeToString(Base62.getInstance().decode(sm2PrivateKey));
System.out.println("Base64：" + privateKeyBase64);
//        sm2PrivateKey="1kRebqILWk9D7U8NiRRyAzQIUEqap3XuO1daMfj8taF3DzNV7fJjc4z5xSWvn6XQ8jWSAuVVCYbEainyCu6wkvZ7gxO88DfkSczoLr8kKeMWfyhDRi6a1KNq2B8LhlrtvJTkgsndqxCWydnl5KLyp6fNC4M0qifGPpWeSMFVEWG2utObf9GjAmkIKUdK1vKZf0xyZZ0s87";
//sm2PublicKey="ZCbp55Y7NeQukYaMlr6XHd2FAAuCQSdXNNXMOKDPswmsOqlWFUzDQ1c7VK45TTNNY9LbP5vVF7P87UHmlYHr43jGW2qlNnnmRXtFJEgOIeC9O1zyZPxI7oCvmG";
//

String src = "test23456789034567";
        // 签名方加签
        System.out.println("签名前数据src：" + src);
        String sign = sign(sm2PrivateKey, src);
        System.out.println("签名sign：" + sign);

        // 验签方验签
        boolean result = verifySign(sm2PublicKey, sign, src);
        System.out.println("验签结果：" + result);



    }

}
